package common.framework.dsb.proxy;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import common.framework.dsb.annotation.DSBService;
import common.framework.dsb.scanner.ResourceScanner;
import common.framework.dsb.service.ServiceConfig;
import common.framework.dsb.service.ServiceDescriptor;
import common.framework.log.Logger;

public class DefaultUploadHandler implements UploadHandler {

	private Map<String, OutputStream> sessionList = new HashMap<String, OutputStream>();
	private Map<String, String> fileList = new HashMap<String, String>();

	private ServiceProxy serviceProxy = null;
	private ServiceConfig serviceConfig = null;

	public DefaultUploadHandler(ServiceProxy serviceProxy, ServiceConfig serviceConfig) {
		super();
		this.serviceProxy = serviceProxy;
		this.serviceConfig = serviceConfig;
	}

	public String openSession(String jarName) throws Exception, RemoteException {
		Logger.log(Logger.FUNCTION_LEVEL, "openSession for: " + jarName);
		String sessionKey = jarName.toLowerCase();
		if (sessionList.containsKey(sessionKey)) {
			throw new Exception("File [" + jarName + "] already being used.");
		}
		String filePath = this.serviceConfig.getServiceDir() + File.separator + "services" + File.separator + jarName;
		File file = new File(filePath);
		if (file.exists()) {
			throw new Exception("File [" + jarName + "] already exists.");
		}
		OutputStream outputStream = new FileOutputStream(filePath, false);
		synchronized (sessionList) {
			sessionList.put(sessionKey, outputStream);
		}
		synchronized (fileList) {
			fileList.put(sessionKey, filePath);
		}
		return sessionKey;
	}

	public void upload(String sessionID, byte[] data, int length) throws Exception, RemoteException {
		OutputStream outputStream = sessionList.get(sessionID);
		if (outputStream == null) {
			throw new Exception("Session [" + sessionID + "] not found.");
		}
		outputStream.write(data, 0, length);
		outputStream.flush();
	}

	public void closeSession(String sessionID) throws Exception, RemoteException {
		OutputStream outputStream = sessionList.get(sessionID);
		if (outputStream == null) {
			throw new Exception("Session [" + sessionID + "] not found.");
		}
		String fileName = fileList.get(sessionID);
		if (fileName == null) {
			throw new Exception("File [" + fileName + "] not found.");
		}
		outputStream.flush();
		outputStream.close();
		synchronized (sessionList) {
			sessionList.remove(sessionID);
		}
		synchronized (fileList) {
			fileList.remove(sessionID);
		}
		Logger.log(Logger.FUNCTION_LEVEL, "file upload success: " + fileName);

		// load jar file to CLASSPATH
		File jarFile = new File(fileName);
		ServiceClassLoader clazzLoader = (ServiceClassLoader) Thread.currentThread().getContextClassLoader();
		// 这一步非常重要，需要把上传的jar文件动态加入到CLASSPATH中
		clazzLoader.addURL(jarFile.toURI().toURL());

		// 扫描jar文件中所有class文件
		ResourceScanner scanner = new ResourceScanner();
		Collection<String> classFiles = scanner.scanArchiveFile(jarFile, ".class");

		for (String classFilePath : classFiles) {
			String fullClassName = fileNameToClassName(classFilePath);
			try {
				// 动态加载类
				Class targetClass = clazzLoader.loadClass(fullClassName);
				// check annotations (DSBService)
				if (targetClass.isAnnotationPresent(DSBService.class)) {
					Annotation annotationObj = targetClass.getAnnotation(DSBService.class);
					DSBService serviceAnnotation = (DSBService) annotationObj;
					ServiceDescriptor descriptor = new ServiceDescriptor();
					descriptor.serviceName = serviceAnnotation.name();
					descriptor.startup = serviceAnnotation.startup();
					Class[] interfaces = targetClass.getInterfaces();
					if (interfaces != null && interfaces.length > 0) {
						// 选取第一interface作为被发布的interface
						Class interfaceClass = interfaces[0];
						descriptor.interfaceClassName = interfaceClass.getName();
						descriptor.serviceClassName = targetClass.getName();
						serviceConfig.addService(descriptor);
					}
					if (descriptor.startup) {
						serviceProxy.startService(descriptor.serviceName);
					}
				}
			} catch (Throwable ex) {
				ex.printStackTrace(System.out);
				Logger.printStackTrace(Logger.FUNCTION_LEVEL, "", ex);
			}
		}// for

	}

	@Override
	public void close() {
		sessionList.clear();
		fileList.clear();
	}

	private Collection<ServiceDescriptor> readDeploymentFile(File file) throws Exception {
		Collection<ServiceDescriptor> result = new ArrayList<ServiceDescriptor>();
		// not implement yet
		return result;
	}

	public static String fileNameToClassName(final String filename) {
		return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
	}

}
